package com.king.factory;

import com.king.annotation.Autowired;
import com.king.annotation.Service;
import com.king.annotation.Transactional;
import com.king.utils.StringUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.reflections.Reflections;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @ClassName: test
 * @Description: TODO
 * @Author: wangchangyong
 * @Date: 2020/4/8 00:36
 * @Version: 1.0.0
 **/
public class AnnotationBeanFactory {

    private static Map<String, Object> map = new HashMap<>();
    private static Map<Class<?>, Object> classMap = new HashMap<>();

    static {
        // 加载xml
        InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("applicationContext.xml");
        try {
            // 解析xml
            Document document = new SAXReader().read(resourceAsStream);
            Element rootElement = document.getRootElement();

            // 获取需要实例化对象的bean
            List<Element> scanList = rootElement.selectNodes("//component-scan");
            for (Element element : scanList) {

                // 取到需要扫描的包名
                String aPackage = element.attributeValue("base-package");
                Reflections f = new Reflections(aPackage);
                // 从包中扫描出包含Service注解的类
                Set<Class<?>> serviceSet = f.getTypesAnnotatedWith(Service.class);

                // 生成Bean
                for (Class<?> aClass : serviceSet) {
                    Service serviceAnnotation = aClass.getAnnotation(Service.class);
                    // bean的Id
                    String beanId = serviceAnnotation.value();
                    if (beanId == null || beanId.length() == 0) {
                        // 类名称小写
                        String simpleName = aClass.getSimpleName();
                        beanId = StringUtils.toLowerCaseFirstOne(simpleName);
                    }
                    map.put(beanId, aClass.newInstance());
                    classMap.put(aClass, beanId);
                }

                // 属性注入
                for (Map.Entry<String, Object> entry : map.entrySet()) {
                    // bean对象
                    Class<?> aClass = entry.getValue().getClass();
                    // bean对象属性
                    Field[] fields = aClass.getDeclaredFields();
                    for (Field field : fields) {
                        Autowired annotation = field.getAnnotation(Autowired.class);
                        if (annotation != null) {
                            // 属性类型（多个相同类型情况未考虑）
                            Class<?> type = field.getType();
                            // 根据类型加载
                            for (Map.Entry<String, Object> obj : map.entrySet()) {
                                if (type.isInstance(obj.getValue())) {
                                    field.setAccessible(true);
                                    try {
                                        field.set(entry.getValue(), obj.getValue());
                                    } catch (IllegalAccessException e) {
                                        e.printStackTrace();
                                    }
                                }
                            }
                        }
                    }
                }

                // 事务代理
                for (Class<?> aClass : serviceSet) {
                    Transactional transactionalAnnotation = aClass.getAnnotation(Transactional.class);
                    if (transactionalAnnotation == null) {
                        continue;
                    }
                    // beanId
                    String beanId = (String) classMap.get(aClass);
                    // 获取类接口
                    Class<?>[] interfaces = aClass.getInterfaces();
                    // 获取代理工厂
                    String proxyId = (String) classMap.get(ProxyFactory.class);
                    ProxyFactory proxyFactory = (ProxyFactory) map.get(proxyId);
                    if (interfaces.length == 0) {
                        //cglib代理
                        Object proxy = proxyFactory.getCglibProxy(map.get(beanId));
                        map.put(beanId, proxy);
                    } else {
                        // jdk代理
                        Object proxy = proxyFactory.getJdkProxy(map.get(beanId));
                        map.put(beanId, proxy);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Object getBean(String id) {
        return map.get(id);
    }
}
